P3354 [IOI2005]Riv 河流
这是一道比较好的树型DP,在参考了网上的一些题解后,蒟蒻终于把它给做了出来
可以看到这道题如果只用二维表示状态明显是不够的
所以设状态fi,j,k表示i为根节点,j为它的一个建有伐木场的父节点,k为i与它的子树共建的伐木场的数量
考虑再开一个数组gi,j,k表示i这个点建了伐木场,状态含义同f
状态转移方程结合代码讲解
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N = 105, M = 205;
int n, k, siz[N], f[N][N][N], g[N][N][N], sum[N], stk[N]; int head[N], ver[M], net[M], edge[M], w[N], idx, top;
void add(int a, int b, int c) { ver[idx] = b, net[idx] = head[a], edge[idx] = c, head[a] = idx++; }
void dfs(int u) { stk[++top] = u; for (int i = head[u]; ~i; i = net[i]) { int v = ver[i]; sum[v] = sum[u] + edge[i]; dfs(v); for (int j = 1; j <= top; j++) { for (int k1 = k; k1 >= 0; k1--)//枚举u一共建的伐木场数量 { f[u][stk[j]][k1] += f[v][stk[j]][0]; g[u][stk[j]][k1] += f[v][u][0];//设置初始状态 for (int k2 = 0; k2 <= k1; k2++)//枚举当前子树建的伐木场数量 { f[u][stk[j]][k1] = min(f[u][stk[j]][k1], f[u][stk[j]][k1 - k2] + f[v][stk[j]][k2]); g[u][stk[j]][k1] = min(g[u][stk[j]][k1], g[u][stk[j]][k1 - k2] + f[v][u][k2]); } } } } //因为上面枚举是假设的是$f$表示没建伐木场,$g$表示建了,并且$g$中$i$这个伐木场并没有算进去,所以下面要减一 for (int j = 1; j <= top; j++) { f[u][stk[j]][0] += w[u] * (sum[u] - sum[stk[j]]); for (int k1 = 1; k1 <= k; k1++) f[u][stk[j]][k1] = min(f[u][stk[j]][k1] + w[u] * (sum[u] - sum[stk[j]]), g[u][stk[j]][k1 - 1]); } top--; }
int main() { scanf("%d%d", &n, &k); memset(head, -1, sizeof(head)); for (int i = 1; i <= n; i++) { int c, v; scanf("%d%d%d", &w[i], &v, &c); add(v, i, c); } dfs(0); printf("%d", f[0][0][k]); return 0; }
|